package state


import "fmt"
import . "api"
import "io/ioutil"
import "stdlib"
// import . "util"


// [-0, +0, v]
// http://www.lua.org/manual/5.3/manual.html#luaL_error
func (self *luaState) Error2(fmt string, a ...interface{}) int {
	self.PushFString(fmt, a...)
	return self.Error()
}


// [-0, +0, v]
// http://www.lua.org/manual/5.3/manual.html#luaL_argerror
// 参数检查方法用于检查传递给Go函数的参数
// 如果非可选参数缺失，或者参数类型和预期不匹配
// 可以调用ArgError()方法抛出错误
func (self *luaState) ArgError(arg int, extraMsg string) int {
	// bad argument #arg to 'funcname' (extramsg)
	return self.Error2("bad argument #%d (%s)", arg, extraMsg)
}


// [-0, +0, v]
// http://www.lua.org/manual/5.3/manual.html#luaL_checkstack
// 调用CheckStack()方法确保栈里还有足够的剩余空间(必要时对栈进行扩容)
// 如果空间不足且扩容失败
// 则调用Error2()方法抛出错误
func (self *luaState) CheckStack2(sz int, msg string) {
	if !self.CheckStack(sz) {
		if msg != "" {
			self.Error2("stack overflow (%s)", msg)
		} else {
			self.Error2("stack overflow")
		}
	}
}


// [-0, +0, v]
// http://www.lua.org/manual/5.3/manual.html#luaL_checkstack
// 通用的参数检查方法
// 第一个参数表示检查是否通过
// 第二个参数表示被检查参数索引
// 第三个参数是附加信息
func (self *luaState) ArgCheck(cond bool, arg int, extraMsg string) {
	if !cond {
		self.ArgError(arg, extraMsg)
	}
}


// [-0, +0, v]
// http://www.lua.org/manual/5.3/manual.html#luaL_argcheck
// 确保某个参数一定存在
func (self *luaState) CheckAny(arg int) {
	if self.Type(arg) == LUA_TNONE {
		self.ArgError(arg, "value expected")
	}
}


// [-0, +0, v]
// http://www.lua.org/manual/5.3/manual.html#luaL_checktype
// 确保某个参数属于指定类型
func (self *luaState) CheckType(arg int, t LuaType) {
	if self.Type(arg) != t {
		self.tagError(arg, t)
	}
}


// [-0, +0, v]
// http://www.lua.org/manual/5.3/manual.html#luaL_checkinteger
// 确保某个参数属于整数, 并且返回该值
func (self *luaState) CheckInteger(arg int) int64 {
	i, ok := self.ToIntegerX(arg)
	if !ok {
		self.intError(arg)
	}
	return i
}


// [-0, +0, v]
// http://www.lua.org/manual/5.3/manual.html#luaL_checknumber
// 确保某个参数属于浮点数, 并且返回该值
func (self *luaState) CheckNumber(arg int) float64 {
	f, ok := self.ToNumberX(arg)
	if !ok {
		self.tagError(arg, LUA_TNUMBER)
	}
	return f
}


// [-0, +0, v]
// http://www.lua.org/manual/5.3/manual.html#luaL_checkstring
// http://www.lua.org/manual/5.3/manual.html#luaL_checklstring
// 确保某个参数属于字符串, 并且返回该值
func (self *luaState) CheckString(arg int) string {
	s, ok := self.ToStringX(arg)
	if !ok {
		self.tagError(arg, LUA_TSTRING)
	}
	return s
}


// [-0, +0, v]
// http://www.lua.org/manual/5.3/manual.html#luaL_optinteger
// 对可选参数进行检查，如果可选参数有值，确保参数值属于指定类型
// 并返回该值，否则返回默认值
func (self *luaState) OptInteger(arg int, def int64) int64 {
	if self.IsNoneOrNil(arg) {
		return def
	}
	return self.CheckInteger(arg)
}


// [-0, +0, v]
// http://www.lua.org/manual/5.3/manual.html#luaL_optnumber
// 对可选参数进行检查，如果可选参数有值，确保参数值属于指定类型
// 并返回该值，否则返回默认值
func (self *luaState) OptNumber(arg int, def float64) float64 {
	if self.IsNoneOrNil(arg) {
		return def
	} 
	return self.CheckNumber(arg)
}


// [-0, +0, v]
// http://www.lua.org/manual/5.3/manual.html#luaL_optstring
// 对可选参数进行检查，如果可选参数有值，确保参数值属于指定类型
// 并返回该值，否则返回默认值
func (self *luaState) OptString(arg int, def string) string {
	if self.IsNoneOrNil(arg) {
		return def
	}
	return self.CheckString(arg)
}


// [-0, +?, e]
// http://www.lua.org/manual/5.3/manual.html#luaL_dofile
// 加载并使用保护模式执行文件
func (self *luaState) DoFile(filename string) bool {
	return self.LoadFile(filename) == LUA_OK &&
		self.PCall(0, LUA_MULTRET, 0) == LUA_OK
}


// [-0, +?, –]
// http://www.lua.org/manual/5.3/manual.html#luaL_dostring
// 加载并使用保护模式执行字符串,
func (self *luaState) DoString(str string) bool {
	return self.LoadString(str) == LUA_OK && 
		self.PCall(0, LUA_MULTRET, 0) == LUA_OK
}


// [-0, +1, m]
// http://www.lua.org/manual/5.3/manual.html#luaL_loadfile
// 以默认方式加载文件
func (self *luaState) LoadFile(filename string) int {
	return self.LoadFileX(filename, "bt")
}


// [-0, +1, m]
// http://www.lua.org/manual/5.3/manual.html#luaL_loadfilex
// 加载文件
func (self *luaState) LoadFileX(filename, mode string) int {
	if data, err := ioutil.ReadFile(filename); err == nil {
		return self.Load(data, "@" + filename, mode)
	}
	return LUA_ERRFILE
}


// [-0, +1, –]
// http://www.lua.org/manual/5.3/manual.html#luaL_loadstring
// 加载字符串
func (self *luaState) LoadString(s string) int {
	return self.Load([]byte(s), s, "bt")
}


// [-0, +0, –]
// http://www.lua.org/manual/5.3/manual.html#luaL_typename
// 返回指定索引处值的类型名
func (self *luaState) TypeName2(idx int) string {
	return self.TypeName(self.Type(idx))
}


// [-0, +0, e]
// http://www.lua.org/manual/5.3/manual.html#luaL_len
// 取指定索引处值的长度, 并以整数形式返还
// 如果获取到的长度不是整数(如果调用了__len元方法, 可能会出现这种情况)则抛出错误
func (self *luaState) Len2(idx int) int64 {
	self.Len(idx)

	i, isNum := self.ToIntegerX(-1)
	if !isNum {
		self.Error2("object length is not an integer")
	}
	self.Pop(1)

	return i
}


// [-0, +1, e]
// http://www.lua.org/manual/5.3/manual.html#luaL_tolstring
// 基础API里的ToString()方法只适用于数字和字符串
// ToString2()方法则适用于任何类型的值
// 此外, ToString2()方法在将值转换为字符串时, 还会优先使用__tostring()元方法
func (self *luaState) ToString2(idx int) string {
	if self.CallMeta(idx, "__tostring") {  // metafield?
		if !self.IsString(-1) {
			self.Error2("'__tostring' must return a string")
		}
	} else {
		switch self.Type(idx) {
		case LUA_TNUMBER:
			if self.IsInteger(idx) {
				self.PushString(fmt.Sprintf("%d", self.ToInteger(idx)))
			} else {
				self.PushString(fmt.Sprintf("%g", self.ToNumber(idx)))  // %g: 用最少的数字来表示
			}
		case LUA_TSTRING:
			self.PushValue(idx)
		case LUA_TBOOLEAN:
			if self.ToBoolean(idx) {
				self.PushString("true")
			} else {
				self.PushString("false")
			}
		case LUA_TNIL:
			self.PushString("nil")
		default:
			tt := self.GetMetafield(idx, "__name")
			var kind string
			if tt == LUA_TSTRING {
				kind = self.CheckString(-1)
			} else {
				kind = self.TypeName2(idx)
			}

			self.PushString(fmt.Sprintf("%s: %p", kind, self.ToPointer(idx)))  // %p: 带0x的指针
			if tt != LUA_TNIL {
				self.Remove(-2)		// remove '__name'
			}
		}
	}
	return self.CheckString(-1)
}


// [-0, +1, e]
// http://www.lua.org/manual/5.3/manual.html#luaL_getsubtable
// 需要將fname对应的table留在栈顶
func (self *luaState) GetSubTable(idx int, fname string) bool {
	if self.GetField(idx, fname) == LUA_TTABLE {
		return true		/* table already there */
	}

	self.Pop(1)	/* remove previous result */
	idx = self.stack.absIndex(idx)
	self.NewTable()
	self.PushValue(-1)		/* copy to be left at top */
	self.SetField(idx, fname)	/* assign new table to field */
	return false		/* false, because did not find table there */
}


// [-0, +(0|1), m]
// http://www.lua.org/manual/5.3/manual.html#luaL_getmetafield
func (self *luaState) GetMetafield(obj int, event string) LuaType {
	if !self.GetMetatable(obj) {	/* no metatable? */
		return LUA_TNIL
	}
	self.PushString(event)
	tt := self.RawGet(-2)
	if tt == LUA_TNIL { /* is metafield nil? */
		self.Pop(2) /* remove metatable and metafield */
	} else {
		self.Remove(-2)  /* remove only metatable */
	}
	return tt /* return metafield type */
}


// [-0, +(0|1), e]
// http://www.lua.org/manual/5.3/manual.html#luaL_callmeta
func (self *luaState) CallMeta(obj int, event string) bool {
	obj = self.AbsIndex(obj)
	if self.GetMetafield(obj, event) == LUA_TNIL { /* no metafield? */
		return false
	}
	self.PushValue(obj)
	self.Call(1, 1)
	return true
}


// [-0, +0, e]
// http://www.lua.org/manual/5.3/manual.html#luaL_openlibs
// 开启全部的标准库
func (self *luaState) OpenLibs() {
	libs := map[string]GoFunction {
		"_G":	stdlib.OpenBaseLib,	// OpenBaseLib: 开启基础库
		"math":	stdlib.OpenMathLib,	// 数学库
		"table":	stdlib.OpenTableLib,	// 表库
		"string":	stdlib.OpenStringLib,		// 字符串库
		"utf8":		stdlib.OpenUTF8Lib,		// UTF8库
		"os":		stdlib.OpenOSLib,		// os库
		"package":	stdlib.OpenPackageLib,	// package库
		"coroutine":stdlib.OpenCoroutineLib,	// coroutine库
	}

	for name, fun := range libs {
		self.RequireF(name, fun, true)
		self.Pop(1)  // 将函数调用结果退栈
	}
}


// [-0, +1, e]
// http://www.lua.org/manual/5.3/manual.html#luaL_requiref
func (self *luaState) RequireF(modname string, openf GoFunction, glb bool) {  // glb: global
	self.GetSubTable(LUA_REGISTRYINDEX, "_LOADED")
	self.GetField(-1, modname)  /* LOADED[modname] */
	if !self.ToBoolean(-1) {  /* package not already loaded? */
		self.Pop(1)		/* remove field */
		self.PushGoFunction(openf)
		self.PushString(modname)  /* argument to open function */
		self.Call(1, 1)		/* call 'openf' to open module */
		self.PushValue(-1)	/* make copy of module(call result) */
		self.SetField(-3, modname)	/* _LOADED[modname] = module */
	}
	self.Remove(-2)	/* remove _LOADED table */
	if glb {
		self.PushValue(-1)	/* copy of module */
		self.SetGlobal(modname)	/* _G[modname] = module */
	}
}


// [-0, +1, m]
// http://www.lua.org/manual/5.3/manual.html#luaL_newlib
func (self *luaState) NewLib(l FuncReg) {
	self.NewLibTable(l)
	self.SetFuncs(l, 0)
}


// [-0, +1, m]
// http://www.lua.org/manual/5.3/manual.html#luaL_newlibtable
func (self *luaState) NewLibTable(l FuncReg) {
	self.CreateTable(0, len(l))
}


// [-nup, +0, m]
// http://www.lua.org/manual/5.3/manual.html#luaL_setfuncs
func (self *luaState) SetFuncs(l FuncReg, nup int) {
	self.CheckStack2(nup, "too many upvalues")
	for name, fun := range l { /* fill the table with given functions */
		for i := 0; i < nup; i++ {  /* copy upvalues to the top */
			self.PushValue(-nup)  // 将栈的第-nup个元素推入栈顶
		}
		// r[-(nup+2)][name]=fun
		// 会将上述循环中加入栈顶的元素退出栈顶
		self.PushGoClosure(fun, nup)  /* closure with those upvalues */
		self.SetField(-(nup+2), name)  // 在PushGoClosure时, PushValue的值已经全部从栈中退出, -(nup+2)是否有误?
	}

	// 将进入函数时推入栈顶的upvalue移出栈顶
	self.Pop(nup)	/* remove upvalues */
}


// [-0, +1, e]
// http://www.lua.org/manual/5.3/manual.html#luaL_requiref
func (self *luaState) intError(arg int) {
	if self.IsNumber(arg) {
		self.ArgError(arg, "number has no integer representation")
	} else {
		self.tagError(arg, LUA_TNUMBER)
	}
}


func (self *luaState) tagError(arg int, tag LuaType) {
	self.typeError(arg, self.TypeName(LuaType(tag)))
}


func (self *luaState) typeError(arg int, tname string) int {
	var typeArg string  /* name for the type of the actual argument */
	if self.GetMetafield(arg, "__name") == LUA_TSTRING {
		typeArg = self.ToString(-1)
	} else if self.Type(arg) == LUA_TLIGHTUSERDATA {
		typeArg = "light userdata" /* special name for messages */
	} else {
		typeArg = self.TypeName2(arg)	/* standard name */
	}
	msg := tname + " expected, got " + typeArg
	self.PushString(msg)  // TODO ? Error2里有PushValue操作, 这个是否多余?
	return self.ArgError(arg, msg) 
}